#include <Render/Include/RendererOGL.h>

#include <Render/Include/RenderObjectOGL.h>

#include <fstream>

#include <glm/gtc/matrix_transform.hpp> // glm::translate, glm::rotate, glm::scale, glm::perspective
#include <glm/gtc/type_ptr.hpp> // glm::value_ptr

using namespace Render;

// ========== LIFECYCLE ========== //

RendererOGL::RendererOGL(IWinWindow* _window, IWinWindow* auxi_window)
    :Renderer()
{
	this->window = _window;
	if (!this->initializeExtensions(auxi_window))
	{
		throw -1;
	}
	
	int attributeListInt[19];
	int pixelFormat[1];
	unsigned int formatCount;
	int result;
    PIXELFORMATDESCRIPTOR pixelFormatDescriptor;
	int attributeList[5];
	char *vendorString, *rendererString;


	// Get the device context for this window.
	m_deviceContext = GetDC(this->window->getHWND());
	if(!m_deviceContext)
	{
		throw -1;
	}
	
	// Support for OpenGL rendering.
	attributeListInt[0] = WGL_SUPPORT_OPENGL_ARB;
	attributeListInt[1] = TRUE;

	// Support for rendering to a window.
	attributeListInt[2] = WGL_DRAW_TO_WINDOW_ARB;
	attributeListInt[3] = TRUE;

	// Support for hardware acceleration.
	attributeListInt[4] = WGL_ACCELERATION_ARB;
	attributeListInt[5] = WGL_FULL_ACCELERATION_ARB;

	// Support for 24bit color.
	attributeListInt[6] = WGL_COLOR_BITS_ARB;
	attributeListInt[7] = 24;

	// Support for 24 bit depth buffer.
	attributeListInt[8] = WGL_DEPTH_BITS_ARB;
	attributeListInt[9] = 24;

	// Support for double buffer.
	attributeListInt[10] = WGL_DOUBLE_BUFFER_ARB;
	attributeListInt[11] = TRUE;

	// Support for swapping front and back buffer.
	attributeListInt[12] = WGL_SWAP_METHOD_ARB;
	attributeListInt[13] = WGL_SWAP_EXCHANGE_ARB;

	// Support for the RGBA pixel type.
	attributeListInt[14] = WGL_PIXEL_TYPE_ARB;
	attributeListInt[15] = WGL_TYPE_RGBA_ARB;

	// Support for a 8 bit stencil buffer.
	attributeListInt[16] = WGL_STENCIL_BITS_ARB;
	attributeListInt[17] = 8;

	// Null terminate the attribute list.
	attributeListInt[18] = 0;
	
	// Query for a pixel format that fits the attributes we want.
	result = wglChoosePixelFormatARB(m_deviceContext, attributeListInt, NULL, 1, pixelFormat, &formatCount);
	if(result != 1)
	{
		throw -1;
	}

	// If the video card/display can handle our desired pixel format then we set it as the current one.
	result = SetPixelFormat(m_deviceContext, pixelFormat[0], &pixelFormatDescriptor);
	if(result != 1)
	{
		throw -1;
	}

	// Set the 3.3 version of OpenGL in the attribute list.
	attributeList[0] = WGL_CONTEXT_MAJOR_VERSION_ARB;
	attributeList[1] = 3;
	attributeList[2] = WGL_CONTEXT_MINOR_VERSION_ARB;
	attributeList[3] = 3;

	// Null terminate the attribute list.
	attributeList[4] = 0;

	// Create a OpenGL 4.0 rendering context.
	m_renderingContext = wglCreateContextAttribsARB(m_deviceContext, 0, attributeList);
	if(m_renderingContext == NULL)
	{
		throw -1;
	}

	// Set the rendering context to active.
	result = wglMakeCurrent(m_deviceContext, m_renderingContext);
	if(result != 1)
	{
		throw -1;
	}
	
	// Set the depth buffer to be entirely cleared to 1.0 values.
	glClearDepth(1.0f);

	// Enable depth testing.
	glEnable(GL_DEPTH_TEST);
	
	// Set the polygon winding to front facing for the left handed system.
	glFrontFace(GL_CW);

	// Enable back face culling.
	glEnable(GL_CULL_FACE);
	glCullFace(GL_BACK);

	// Initialize the world/model matrix to the identity matrix.
	/*
	BuildIdentityMatrix(m_worldMatrix);

	// Set the field of view and screen aspect ratio.
	fieldOfView = 3.14159265358979323846f / 4.0f;
	screenAspect = (float)screenWidth / (float)screenHeight;

	// Build the perspective projection matrix.
	BuildPerspectiveFovLHMatrix(m_projectionMatrix, fieldOfView, screenAspect, screenNear, screenDepth);
	*/

	// Get the name of the video card.
	vendorString = (char*)glGetString(GL_VENDOR);
	rendererString = (char*)glGetString(GL_RENDERER);

	// Store the video card name in a class member variable so it can be retrieved later.
	strcpy_s(m_videoCardDescription, vendorString);
	strcat_s(m_videoCardDescription, " - ");
	strcat_s(m_videoCardDescription, rendererString);

	// Turn on or off the vertical sync depending on the input bool value.
	if (false)//(vsync)
	{
		result = wglSwapIntervalEXT(1);
	}
	else
	{
		result = wglSwapIntervalEXT(0);
	}

	// Check if vsync was set correctly.
	if(result != 1)
	{
		throw -1;
	}

	// everything ok

	bool result_shader;


	// Initialize the vertex and pixel shaders.
	result_shader = this->initializeShader("color.vs", "color.ps");
	if(!result_shader)
	{
		throw -1;
	}
	
	/// creates the default objects used for rendering
    this->createSphereRenderObject();
    this->createCubeRenderObject();    
}

RendererOGL::~RendererOGL(void)
{
    delete this->sphere_render_object;
    delete this->cube_render_object;
	/*// Disable the two vertex array attributes.
	OpenGL->glDisableVertexAttribArray(0);
	OpenGL->glDisableVertexAttribArray(1);
	
	// Release the vertex buffer.
	OpenGL->glBindBuffer(GL_ARRAY_BUFFER, 0);
	OpenGL->glDeleteBuffers(1, &m_vertexBufferId);

	// Release the index buffer.
	OpenGL->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
	OpenGL->glDeleteBuffers(1, &m_indexBufferId);

	// Release the vertex array object.	
	OpenGL->glBindVertexArray(0);
	OpenGL->glDeleteVertexArrays(1, &m_vertexArrayId);*/
    
	// Detach the vertex and fragment shaders from the program.
	this->glDetachShader(m_shaderProgram, m_vertexShader);
	this->glDetachShader(m_shaderProgram, m_fragmentShader);

	// Delete the vertex and fragment shaders.
	this->glDeleteShader(m_vertexShader);
	this->glDeleteShader(m_fragmentShader);

	// Delete the shader program.
	this->glDeleteProgram(m_shaderProgram);
	
	// Release the rendering context.
	if(m_renderingContext)
	{
		wglMakeCurrent(NULL, NULL);
		wglDeleteContext(m_renderingContext);
		m_renderingContext = 0;
	}

	// Release the device context.
	if(m_deviceContext)
	{
		ReleaseDC(this->window->getHWND(), m_deviceContext);
		m_deviceContext = 0;
	}
}

// ========== OVERLODED OPERATORS ========== //

// ========== METHODS ========== //

void RendererOGL::renderScene(Model::Scene* scene)
{
	// Set the color to clear the screen to.
	glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 

	// Clear the screen and depth buffer.
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	this->glUseProgram(m_shaderProgram);

	this->configViewPoint(scene);

	this->renderSceneWalls(scene);

    const Model::Snake* snake = scene->getSnake();

    const Model::Element* head_element = snake->getHeadElement();
    this->renderSnakeHead(head_element, scene);

    const Model::Element* body_element = head_element->getNextElement();
    while (NULL != body_element)
    {
        this->renderSnakeBodyElement(body_element,scene);
        body_element = body_element->getNextElement();
    }
        
    const Model::Element* free_element = scene->getFreeElement();
    this->renderSnakeBodyElement(free_element, scene);
	
	// Present the back buffer to the screen since rendering is complete.
	SwapBuffers(m_deviceContext);
}

void RendererOGL::renderBlack()
{
  /*  
    HRESULT hr;
    hr = this->p_device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, (D3DCOLOR)0x00000000, 1.0f, 0x00000000);
    this->drawText();
    this->p_device->Present(NULL, NULL, NULL, NULL);
    this->clearText();
    */
}

// ========== GETTERS ========== //

// ========== SETTERS ========== //

// ========== INQUIRY ========== //

// ========== INTERNAL METHODS ========== //

void RendererOGL::createSphereRenderObject()
{
    unsigned int rings = 20;
    unsigned int segments = 20;
    // it is an sphere of radio 1 ---> float radio = 1.0f;

    // I add the two poles
    unsigned int vertex_count = rings * segments + 2;
    VertexData* vertices = new VertexData[vertex_count];

    // south pole
    vertices[0].x = 0.0f;
	vertices[0].y =	0.0f;
	vertices[0].z =	-1.0f;
    // north pole
    vertices[vertex_count - 1].x =  0.0f;
	vertices[vertex_count - 1].y =	0.0f;
	vertices[vertex_count - 1].z =	1.0f;
    
    float M_PI = 3.14159f;

    float delta_phi = M_PI / (float)( rings + 1 );
    float delta_theta = 2.0f * M_PI / (float)segments;
    
    unsigned int vertex_pos = 1;
    float phi = - M_PI;
    for (unsigned int i = 0; i < rings; i++ )
    {
        phi += delta_phi;
        float theta = 0.0f;
        for (unsigned int j = 0; j < segments; j++ )
        {
            vertices[vertex_pos].x = cos(theta) * cos(phi);
			vertices[vertex_pos].y = sin(theta) * cos(phi);
			vertices[vertex_pos].z = sin(phi);
            theta += delta_theta;
            vertex_pos++;
        }
    }

    unsigned int triangles_count = segments * 2 + segments * (rings - 1) * 2;
    unsigned int index_count  = triangles_count * 3;
    unsigned int* indices = new unsigned int[index_count];
    unsigned int index_pos = 0;

    /// south pole triangles
    for (unsigned int i = 0; i < segments; i++)
    {
        indices[index_pos++] = 1 + i;
        indices[index_pos++] = 0; // south pole
        indices[index_pos++] = 1 + ((i + 1) % segments);
    }

    /// between rings triangles
    for (unsigned int i = 0; i < rings - 1; i++ )
    {
        for (unsigned int j = 0; j < segments; j++ )
        {
            indices[index_pos++] = 1 + i * segments + j;
            indices[index_pos++] = 1 + (i + 1) * segments + j;
            indices[index_pos++] = 1 + (i + 1) * segments + ( (j + 1) % segments );

            indices[index_pos++] = 1 + i * segments + j;
            indices[index_pos++] = 1 + (i + 1) * segments + ( (j + 1) % segments );
            indices[index_pos++] = 1 + i * segments + ( (j + 1) % segments ); 
        }
    }

    /// north pole triangles
    for (unsigned int j = 0; j < segments; j++)
    {
        indices[index_pos++] = 1 + (rings - 1) * segments + j;
        indices[index_pos++] = vertex_count - 1;
        indices[index_pos++] = 1 + (rings - 1) * segments + ( (j + 1) % segments ); 
    }

    //ASSERT(index_pos == index_count);
	
	unsigned int m_vertexArrayId, m_vertexBufferId, m_indexBufferId;
	// Allocate an OpenGL vertex array object.
	this->glGenVertexArrays(1, &m_vertexArrayId);

	// Bind the vertex array object to store all the buffers and vertex attributes we create here.
	this->glBindVertexArray(m_vertexArrayId);

	// Generate an ID for the vertex buffer.
	this->glGenBuffers(1, &m_vertexBufferId);

	// Bind the vertex buffer and load the vertex (position and color) data into the vertex buffer.
	this->glBindBuffer(GL_ARRAY_BUFFER, m_vertexBufferId);
	this->glBufferData(GL_ARRAY_BUFFER, vertex_count * sizeof(VertexData), vertices, GL_STATIC_DRAW);

	// Enable the one vertex array attributes.
	this->glEnableVertexAttribArray(0);  // Vertex position.
	
	// Specify the location and format of the position portion of the vertex buffer.
	this->glBindBuffer(GL_ARRAY_BUFFER, m_vertexBufferId);
	this->glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(VertexData), 0);

	// Generate an ID for the index buffer.
	this->glGenBuffers(1, &m_indexBufferId);

	// Bind the index buffer and load the index data into it.
	this->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBufferId);
	this->glBufferData(GL_ELEMENT_ARRAY_BUFFER, index_count* sizeof(unsigned int), indices, GL_STATIC_DRAW);
	
	// Now that the buffers have been loaded we can release the array data.
	delete [] vertices;
	vertices = 0;

	delete [] indices;
	indices = 0;

	this->sphere_render_object = new RenderObjectOGL(vertex_count, index_count, m_vertexArrayId, m_vertexBufferId, m_indexBufferId);
}

void RendererOGL::createCubeRenderObject()
{
    // Create vertex buffer
    VertexData vertices[] =
    {
		{ -0.5f, 0.5f, -0.5f },
		{ -0.5f, 0.5f, 0.5f  },
		{ 0.5f, 0.5f, 0.5f   },
		{ 0.5f, 0.5f, -0.5f  },
		{ -0.5f, -0.5f, -0.5f },
		{ -0.5f, -0.5f, 0.5f },
		{ 0.5f, -0.5f, 0.5f },
		{ 0.5f, -0.5f, -0.5f }
    };
    
    unsigned int indices[] = 
    {
        0, 1, 2,
        0, 2, 3,
        3, 2, 6,
        3, 6, 7,
        0, 3, 7,
        0, 7, 4,
        1, 5, 6,
        1, 6, 2,
        0, 4, 5, 
        0, 5, 1,
        7, 6, 5, 
        7, 5, 4
    };
	int vertex_count = 8;
	int index_count = 36;
    unsigned int m_vertexArrayId, m_vertexBufferId, m_indexBufferId;
	// Allocate an OpenGL vertex array object.
	this->glGenVertexArrays(1, &m_vertexArrayId);

	// Bind the vertex array object to store all the buffers and vertex attributes we create here.
	this->glBindVertexArray(m_vertexArrayId);

	// Generate an ID for the vertex buffer.
	this->glGenBuffers(1, &m_vertexBufferId);

	// Bind the vertex buffer and load the vertex (position and color) data into the vertex buffer.
	this->glBindBuffer(GL_ARRAY_BUFFER, m_vertexBufferId);
	this->glBufferData(GL_ARRAY_BUFFER, vertex_count * sizeof(VertexData), vertices, GL_STATIC_DRAW);

	// Enable the one vertex array attributes.
	this->glEnableVertexAttribArray(0);  // Vertex position.
	
	// Specify the location and format of the position portion of the vertex buffer.
	this->glBindBuffer(GL_ARRAY_BUFFER, m_vertexBufferId);
	this->glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(VertexData), 0);

	// Generate an ID for the index buffer.
	this->glGenBuffers(1, &m_indexBufferId);

	// Bind the index buffer and load the index data into it.
	this->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBufferId);
	this->glBufferData(GL_ELEMENT_ARRAY_BUFFER, index_count* sizeof(unsigned int), indices, GL_STATIC_DRAW);
	
	this->cube_render_object = new RenderObjectOGL(vertex_count, index_count, m_vertexArrayId, m_vertexBufferId, m_indexBufferId);
}


void RendererOGL::configViewPoint(Model::Scene* scene)
{
    /// set the viewpoint
  
    glm::mat4 view_matrix;
    glm::vec3 camera_position;
    glm::vec3 camera_at;
    glm::vec3 camera_up;
    if (this->render_3D)
    {
        const Model::Snake* snake = scene->getSnake();
        const Model::Element* head_element = snake->getHeadElement();
        int at_x = 0;
        int at_y = 0;
        snake->getDirection(&at_x, &at_y);
        camera_position = glm::vec3((float)(head_element->getXPosition() - at_x * 5), (float)(head_element->getYPosition() - at_y * 5), 5.0f);
        camera_at = glm::vec3((float)(head_element->getXPosition() + at_x), (float)(head_element->getYPosition() + at_y), 0.0f);
        camera_up = glm::vec3(0.0f, 0.0f, 1.0f);
    }
    else
    {
        camera_position = glm::vec3((float)scene->getWidth()/2.0f, (float)scene->getHeight() / 2.0f, 20.0f); 
        camera_at = glm::vec3((float)scene->getWidth()/2.0f, (float)scene->getHeight() / 2.0f, 0.0f); 
        camera_up = glm::vec3(0.0f, 1.0f, 0.0f);
    }
    
	view_matrix = glm::lookAt(camera_position, camera_at, camera_up);
	
	unsigned int location;
	// Set the view matrix in the vertex shader.
	location = this->glGetUniformLocation(m_shaderProgram, "viewMatrix");
	if(location == -1)
	{
		throw -1;
	}
	this->glUniformMatrix4fv(location, 1, false, glm::value_ptr(view_matrix));

    /// set the projection
    glm::mat4 projection_matrix;
    if (this->render_3D)
    {
		projection_matrix = glm::perspective(90.0f/*3.14f/2.0f*/, 1.0f, 0.6f, 1000.0f);
    }
    else
    {
        projection_matrix = glm::ortho( -(float)scene->getWidth() / 2.0f - 2.5f,  (float)scene->getWidth() / 2.0f + 2.5f,
										-(float)scene->getHeight() / 2.0f - 2.5f, (float)scene->getHeight() / 2.0f + 2.5f,
										1.0f, 1000.0f);
    }
    
    
	// Set the world matrix in the vertex shader.
	location = this->glGetUniformLocation(m_shaderProgram, "projectionMatrix");
	if(location == -1)
	{
		throw -1;
	}
	this->glUniformMatrix4fv(location, 1, false, glm::value_ptr(projection_matrix));
    
}

void RendererOGL::renderSceneWalls(Model::Scene* scene)
{
    unsigned int location_world_matrix, location_color;
	// Set the world matrix in the vertex shader.
	location_world_matrix = this->glGetUniformLocation(m_shaderProgram, "worldMatrix");
	if(location_world_matrix == -1)
	{
		throw -1;
	}
	location_color = this->glGetUniformLocation(m_shaderProgram, "object_color");
	if(location_color == -1)
	{
		throw -1;
	}

	RenderObjectOGL* mesh = reinterpret_cast<RenderObjectOGL*>(this->cube_render_object);
	glm::vec3 color = glm::vec3(1.0f, 1.0f, 1.0f);
	this->glUniform3fv(location_color, 1, glm::value_ptr(color)); 

	/// set the transformation matrix for the walls
    glm::mat4 scale_matrix;
    glm::mat4 position_matrix;
    glm::mat4 world_matrix;
	
    //vertical walls
	scale_matrix = glm::scale(glm::mat4(1.0f), glm::vec3(1.0f, (float)scene->getHeight() + 2.0f, 1.0f));
	position_matrix = glm::translate(glm::mat4(1.0f), glm::vec3(-1.0f, (float)scene->getHeight() / 2.0f - 0.5f, 0.0f) ); 
    world_matrix = position_matrix * scale_matrix;
    this->glUniformMatrix4fv(location_world_matrix, 1, false, glm::value_ptr(world_matrix));
		
	// Bind the vertex array object that stored all the information about the vertex and index buffers.
	this->glBindVertexArray(mesh->m_vertexArrayId);
	// Render the vertex buffer using the index buffer.
	glDrawElements(GL_TRIANGLES, mesh->m_indexCount, GL_UNSIGNED_INT, 0);

	
	position_matrix = glm::translate(glm::mat4(1.0f), glm::vec3( (float)scene->getWidth(), (float)scene->getHeight() / 2.0f - 0.5f, 0.0f));
    world_matrix = position_matrix * scale_matrix;
    this->glUniformMatrix4fv(location_world_matrix, 1, false, glm::value_ptr(world_matrix));
		
	// Bind the vertex array object that stored all the information about the vertex and index buffers.
	this->glBindVertexArray(mesh->m_vertexArrayId);
	// Render the vertex buffer using the index buffer.
	glDrawElements(GL_TRIANGLES, mesh->m_indexCount, GL_UNSIGNED_INT, 0);


	//horizontal walls
    scale_matrix = glm::scale(glm::mat4(1.0f), glm::vec3( (float)scene->getWidth()  + 2.0f, 1.0f, 1.0f));
    position_matrix = glm::translate(glm::mat4(1.0f), glm::vec3((float)scene->getWidth() / 2.0f - 0.5f, -1.0f, 0.0f));
    world_matrix = position_matrix * scale_matrix;
    this->glUniformMatrix4fv(location_world_matrix, 1, false, glm::value_ptr(world_matrix));
		
	// Bind the vertex array object that stored all the information about the vertex and index buffers.
	this->glBindVertexArray(mesh->m_vertexArrayId);
	// Render the vertex buffer using the index buffer.
	glDrawElements(GL_TRIANGLES, mesh->m_indexCount, GL_UNSIGNED_INT, 0);

    position_matrix = glm::translate(glm::mat4(1.0f), glm::vec3((float)scene->getWidth() / 2.0f - 0.5f, (float)scene->getHeight(), 0.0f));
    world_matrix = position_matrix * scale_matrix;
    this->glUniformMatrix4fv(location_world_matrix, 1, false, glm::value_ptr(world_matrix));
		
	// Bind the vertex array object that stored all the information about the vertex and index buffers.
	this->glBindVertexArray(mesh->m_vertexArrayId);
	// Render the vertex buffer using the index buffer.
	glDrawElements(GL_TRIANGLES, mesh->m_indexCount, GL_UNSIGNED_INT, 0);

}

void RendererOGL::renderSnakeHead(const Model::Element* &element, Model::Scene* scene)
{
    unsigned int location_world_matrix, location_color;
	// Set the world matrix in the vertex shader.
	location_world_matrix = this->glGetUniformLocation(m_shaderProgram, "worldMatrix");
	if(location_world_matrix == -1)
	{
		throw -1;
	}
	location_color = this->glGetUniformLocation(m_shaderProgram, "object_color");
	if(location_color == -1)
	{
		throw -1;
	}

	RenderObjectOGL* mesh = reinterpret_cast<RenderObjectOGL*>(this->sphere_render_object);
	glm::vec3 color = glm::vec3(0.0f, 1.0f, 0.0f);
	this->glUniform3fv(location_color, 1, glm::value_ptr(color)); 

	/// set the transformation matrix 
    glm::mat4 scale_matrix;
    glm::mat4 position_matrix;
    glm::mat4 world_matrix;
	
   	scale_matrix = glm::scale(glm::mat4(1.0f), glm::vec3(0.45f, 0.45f, 0.45f));
	position_matrix = glm::translate(glm::mat4(1.0f), glm::vec3( scene->getWidth() - element->getXPosition(), element->getYPosition(), 0.0f) ); 
    world_matrix = position_matrix * scale_matrix;
    this->glUniformMatrix4fv(location_world_matrix, 1, false, glm::value_ptr(world_matrix));
		
	// Bind the vertex array object that stored all the information about the vertex and index buffers.
	this->glBindVertexArray(mesh->m_vertexArrayId);
	// Render the vertex buffer using the index buffer.
	glDrawElements(GL_TRIANGLES, mesh->m_indexCount, GL_UNSIGNED_INT, 0);

}

void RendererOGL::renderSnakeBodyElement(const Model::Element* &element, Model::Scene* scene)
{
	unsigned int location_world_matrix, location_color;
	// Set the world matrix in the vertex shader.
	location_world_matrix = this->glGetUniformLocation(m_shaderProgram, "worldMatrix");
	if(location_world_matrix == -1)
	{
		throw -1;
	}
	location_color = this->glGetUniformLocation(m_shaderProgram, "object_color");
	if(location_color == -1)
	{
		throw -1;
	}

	RenderObjectOGL* mesh = reinterpret_cast<RenderObjectOGL*>(this->cube_render_object);
	glm::vec3 color = glm::vec3(0.0f, 0.0f, 1.0f);
	this->glUniform3fv(location_color, 1, glm::value_ptr(color)); 

	/// set the transformation matrix 
    glm::mat4 scale_matrix;
    glm::mat4 position_matrix;
    glm::mat4 world_matrix;
	
	scale_matrix = glm::scale(glm::mat4(1.0f), glm::vec3(0.9f, 0.9f, 0.9f));
	position_matrix = glm::translate(glm::mat4(1.0f), glm::vec3(scene->getWidth() - element->getXPosition(), element->getYPosition(), 0.0f) ); 
    world_matrix = position_matrix * scale_matrix;
    this->glUniformMatrix4fv(location_world_matrix, 1, false, glm::value_ptr(world_matrix));
		
	// Bind the vertex array object that stored all the information about the vertex and index buffers.
	this->glBindVertexArray(mesh->m_vertexArrayId);
	// Render the vertex buffer using the index buffer.
	glDrawElements(GL_TRIANGLES, mesh->m_indexCount, GL_UNSIGNED_INT, 0);
}

void RendererOGL::drawText()
{
    /*
    HRESULT hr;
    RECT rc;    
    for (unsigned int i = 0; i < this->text_to_render.size(); i++)
    {
        SetRect(&rc, this->text_to_render[i]->pos_x, this->text_to_render[i]->pos_y, 200, 200 );
        hr = font->DrawTextA(NULL, this->text_to_render[i]->text.c_str(), -1, &rc, DT_NOCLIP, D3DCOLOR_RGBA(255, 0, 0, 255));         
    }
    */
}


bool RendererOGL::initializeShader(char* vsFilename, char* fsFilename)
{
	const char* vertexShaderBuffer;
	const char* fragmentShaderBuffer;
	int status;


	// Load the vertex shader source file into a text buffer.
	vertexShaderBuffer = this->loadShaderSourceFile(vsFilename);
	if(!vertexShaderBuffer)
	{
		return false;
	}

	// Load the fragment shader source file into a text buffer.
	fragmentShaderBuffer = this->loadShaderSourceFile(fsFilename);
	if(!fragmentShaderBuffer)
	{
		return false;
	}

	// Create a vertex and fragment shader object.
	m_vertexShader = this->glCreateShader(GL_VERTEX_SHADER);
	m_fragmentShader = this->glCreateShader(GL_FRAGMENT_SHADER);

	// Copy the shader source code strings into the vertex and fragment shader objects.
	this->glShaderSource(m_vertexShader, 1, &vertexShaderBuffer, NULL);
	this->glShaderSource(m_fragmentShader, 1, &fragmentShaderBuffer, NULL);

	// Release the vertex and fragment shader buffers.
	delete [] vertexShaderBuffer;
	vertexShaderBuffer = 0;
	
	delete [] fragmentShaderBuffer;
	fragmentShaderBuffer = 0;

	// Compile the shaders.
	this->glCompileShader(m_vertexShader);
	this->glCompileShader(m_fragmentShader);

	// Check to see if the vertex shader compiled successfully.
	this->glGetShaderiv(m_vertexShader, GL_COMPILE_STATUS, &status);
	if(status != 1)
	{
		// If it did not compile then write the syntax error message out to a text file for review.
		//OutputShaderErrorMessage(OpenGL, hwnd, m_vertexShader, vsFilename);
		return false;
	}

	// Check to see if the fragment shader compiled successfully.
	this->glGetShaderiv(m_fragmentShader, GL_COMPILE_STATUS, &status);
	if(status != 1)
	{
		// If it did not compile then write the syntax error message out to a text file for review.
		//OutputShaderErrorMessage(OpenGL, hwnd, m_fragmentShader, fsFilename);
		return false;
	}

	// Create a shader program object.
	m_shaderProgram = this->glCreateProgram();

	// Attach the vertex and fragment shader to the program object.
	this->glAttachShader(m_shaderProgram, m_vertexShader);
	this->glAttachShader(m_shaderProgram, m_fragmentShader);

	// Bind the shader input variables.
	this->glBindAttribLocation(m_shaderProgram, 0, "inputPosition");
	
	// Link the shader program.
	this->glLinkProgram(m_shaderProgram);

	// Check the status of the link.
	this->glGetProgramiv(m_shaderProgram, GL_LINK_STATUS, &status);
	if(status != 1)
	{
		// If it did not link then write the syntax error message out to a text file for review.
		//OutputLinkerErrorMessage(OpenGL, hwnd, m_shaderProgram);
		return false;
	}

	return true;
}


char* RendererOGL::loadShaderSourceFile(char* filename)
{
	std::ifstream fin;
	int fileSize;
	char input;
	char* buffer;


	// Open the shader source file.
	fin.open(filename);

	// If it could not open the file then exit.
	if(fin.fail())
	{
		return 0;
	}

	// Initialize the size of the file.
	fileSize = 0;

	// Read the first element of the file.
	fin.get(input);

	// Count the number of elements in the text file.
	while(!fin.eof())
	{
		fileSize++;
		fin.get(input);
	}

	// Close the file for now.
	fin.close();

	// Initialize the buffer to read the shader source file into.
	buffer = new char[fileSize+1];
	if(!buffer)
	{
		return 0;
	}

	// Open the shader source file again.
	fin.open(filename);

	// Read the shader text file into the buffer as a block.
	fin.read(buffer, fileSize);

	// Close the file.
	fin.close();

	// Null terminate the buffer.
	buffer[fileSize] = '\0';

	return buffer;
}

bool RendererOGL::initializeExtensions(IWinWindow* auxi_window)
{
	HDC deviceContext;
	PIXELFORMATDESCRIPTOR pixelFormat;
	int error;
	HGLRC renderContext;
	bool result;


	// Get the device context for this window.
	deviceContext = GetDC(auxi_window->getHWND());
	if(!deviceContext)
	{
		return false;
	}

    // Set a temporary default pixel format.
    error = SetPixelFormat(deviceContext, 1, &pixelFormat);
	if(error != 1)
	{
		return false;
	}

	// Create a temporary rendering context.
	renderContext = wglCreateContext(deviceContext);
	if(!renderContext)
	{
		return false;
	}

	// Set the temporary rendering context as the current rendering context for this window.
	error = wglMakeCurrent(deviceContext, renderContext);
	if(error != 1)
	{
		return false;
	}

	// Initialize the OpenGL extensions needed for this application.  Note that a temporary rendering context was needed to do so.
	result = this->loadExtensionList();
	if(!result)
	{
		return false;
	}

	// Release the temporary rendering context now that the extensions have been loaded.
	wglMakeCurrent(NULL, NULL);
	wglDeleteContext(renderContext);
	renderContext = NULL;

	// Release the device context for this window.
	ReleaseDC(auxi_window->getHWND(), deviceContext);
	deviceContext = 0;

	return true;
}

bool RendererOGL::loadExtensionList()
{
	// Load the OpenGL extensions that this application will be using.
	wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");
	if(!wglChoosePixelFormatARB)
	{
		return false;
	}

	wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB");
	if(!wglCreateContextAttribsARB)
	{
		return false;
	}

	wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT");
	if(!wglSwapIntervalEXT)
	{
		return false;
	}

	glAttachShader = (PFNGLATTACHSHADERPROC)wglGetProcAddress("glAttachShader");
	if(!glAttachShader)
	{
		return false;
	}

	glBindBuffer = (PFNGLBINDBUFFERPROC)wglGetProcAddress("glBindBuffer");
	if(!glBindBuffer)
	{
		return false;
	}

	glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC)wglGetProcAddress("glBindVertexArray");
	if(!glBindVertexArray)
	{
		return false;
	}

	glBufferData = (PFNGLBUFFERDATAPROC)wglGetProcAddress("glBufferData");
	if(!glBufferData)
	{
		return false;
	}

	glCompileShader = (PFNGLCOMPILESHADERPROC)wglGetProcAddress("glCompileShader");
	if(!glCompileShader)
	{
		return false;
	}

	glCreateProgram = (PFNGLCREATEPROGRAMPROC)wglGetProcAddress("glCreateProgram");
	if(!glCreateProgram)
	{
		return false;
	}

	glCreateShader = (PFNGLCREATESHADERPROC)wglGetProcAddress("glCreateShader");
	if(!glCreateShader)
	{
		return false;
	}

	glDeleteBuffers = (PFNGLDELETEBUFFERSPROC)wglGetProcAddress("glDeleteBuffers");
	if(!glDeleteBuffers)
	{
		return false;
	}

	glDeleteProgram = (PFNGLDELETEPROGRAMPROC)wglGetProcAddress("glDeleteProgram");
	if(!glDeleteProgram)
	{
		return false;
	}

	glDeleteShader = (PFNGLDELETESHADERPROC)wglGetProcAddress("glDeleteShader");
	if(!glDeleteShader)
	{
		return false;
	}

	glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC)wglGetProcAddress("glDeleteVertexArrays");
	if(!glDeleteVertexArrays)
	{
		return false;
	}

	glDetachShader = (PFNGLDETACHSHADERPROC)wglGetProcAddress("glDetachShader");
	if(!glDetachShader)
	{
		return false;
	}

	glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)wglGetProcAddress("glEnableVertexAttribArray");
	if(!glEnableVertexAttribArray)
	{
		return false;
	}

	glGenBuffers = (PFNGLGENBUFFERSPROC)wglGetProcAddress("glGenBuffers");
	if(!glGenBuffers)
	{
		return false;
	}

	glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC)wglGetProcAddress("glGenVertexArrays");
	if(!glGenVertexArrays)
	{
		return false;
	}

	glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC)wglGetProcAddress("glGetAttribLocation");
	if(!glGetAttribLocation)
	{
		return false;
	}

	glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)wglGetProcAddress("glGetProgramInfoLog");
	if(!glGetProgramInfoLog)
	{
		return false;
	}

	glGetProgramiv = (PFNGLGETPROGRAMIVPROC)wglGetProcAddress("glGetProgramiv");
	if(!glGetProgramiv)
	{
		return false;
	}

	glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)wglGetProcAddress("glGetShaderInfoLog");
	if(!glGetShaderInfoLog)
	{
		return false;
	}

	glGetShaderiv = (PFNGLGETSHADERIVPROC)wglGetProcAddress("glGetShaderiv");
	if(!glGetShaderiv)
	{
		return false;
	}

	glLinkProgram = (PFNGLLINKPROGRAMPROC)wglGetProcAddress("glLinkProgram");
	if(!glLinkProgram)
	{
		return false;
	}

	glShaderSource = (PFNGLSHADERSOURCEPROC)wglGetProcAddress("glShaderSource");
	if(!glShaderSource)
	{
		return false;
	}

	glUseProgram = (PFNGLUSEPROGRAMPROC)wglGetProcAddress("glUseProgram");
	if(!glUseProgram)
	{
		return false;
	}

	glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)wglGetProcAddress("glVertexAttribPointer");
	if(!glVertexAttribPointer)
	{
		return false;
	}

	glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC)wglGetProcAddress("glBindAttribLocation");
	if(!glBindAttribLocation)
	{
		return false;
	}

	glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)wglGetProcAddress("glGetUniformLocation");
	if(!glGetUniformLocation)
	{
		return false;
	}

	glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC)wglGetProcAddress("glUniformMatrix4fv");
	if(!glUniformMatrix4fv)
	{
		return false;
	}

	glActiveTexture = (PFNGLACTIVETEXTUREPROC)wglGetProcAddress("glActiveTexture");
	if(!glActiveTexture)
	{
		return false;
	}

	glUniform1i = (PFNGLUNIFORM1IPROC)wglGetProcAddress("glUniform1i");
	if(!glUniform1i)
	{
		return false;
	}

	glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC)wglGetProcAddress("glGenerateMipmap");
	if(!glGenerateMipmap)
	{
		return false;
	}

	glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC)wglGetProcAddress("glDisableVertexAttribArray");
	if(!glDisableVertexAttribArray)
	{
		return false;
	}

	glUniform3fv = (PFNGLUNIFORM3FVPROC)wglGetProcAddress("glUniform3fv");
	if(!glUniform3fv)
	{
		return false;
	}

	glUniform4fv = (PFNGLUNIFORM4FVPROC)wglGetProcAddress("glUniform4fv");
	if(!glUniform4fv)
	{
		return false;
	}

	return true;
}
